package com.ruoyi.web.controller.me;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ProcessStopException;
import com.ruoyi.common.utils.ExecUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SseEmitterUtils;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.CommonUtil;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.dm.mapper.DataCollectionMapper;
import com.ruoyi.framework.datasource.DynamicDataSource;
import com.ruoyi.me.service.DifferentAltitudeService;
import com.ruoyi.system.service.ISysDictTypeService;
import com.ruoyi.web.vo.ModelAssessDiffAltVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.File;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * </p>
 *
 * @author liuhl
 * @since 2024-06-25
 */

@RestController
@RequestMapping("/me/differentAltitude")
@Api(value = "DifferentAltitudeController", tags = "模型检验评估-不同高度条件下大气模型适用性评估")
@AllArgsConstructor
@Slf4j
public class DifferentAltitudeController extends BaseController {

    private DynamicDataSource dataSource;
    private DataCollectionMapper dataCollectionMapper;
    private DifferentAltitudeService differentAltitudeService;

    @Autowired
    private ISysDictTypeService dictTypeService;



//    //@PreAuthorize("@ss.hasPermi('me:differentAltitude:testAdd')")
    @ApiOperation(value = "卫星评估数据入库", notes = "卫星评估数据入库")
    @GetMapping("/testAdd")
    public AjaxResult testAdd(@ApiParam(value = "路径", name = "path", required = true)@RequestParam(value = "path") String path) {
        return differentAltitudeService.testAdd(path);
    }
    //@PreAuthorize("@ss.hasPermi('me:differentAltitude:getSatelliteData')")
    @ApiOperation(value = "获取初始统计图以及表格", notes = "获取初始统计图以及表格")
    @GetMapping("/getSatelliteData")
    public AjaxResult getSatelliteData() {
        return differentAltitudeService.getSatelliteData();
    }

    //@PreAuthorize("@ss.hasPermi('me:differentAltitude:getCalculateResult')")
    @ApiOperation(value = "获取计算评估结果", notes = "获取计算评估结果")
    @PostMapping("/getCalculateResult")
    public AjaxResult getCalculateResult(@RequestBody Map<String, Object> map) throws ProcessStopException {
       return differentAltitudeService.getCalculateResult(map);
    }

    //@PreAuthorize("@ss.hasPermi('me:differentAltitude:getSatelliteInfo')")
    @SneakyThrows
    @ApiOperation(value = "获取所有卫星信息", notes = "获取所有卫星信息")
    @GetMapping("/getSatelliteInfo")
    public AjaxResult getSatelliteInfo() {
        ////获取模式名
        //String schemaName;
        //Connection connection = null;
        //try {
        //    connection = dataSource.getConnection();
        //    String schema = connection.getSchema();
        //    if (StrUtil.isNotBlank(schema)) {
        //        schemaName = schema;
        //    } else {
        //        schemaName = connection.getMetaData().getUserName();
        //    }
        //    List<String> tableNames = dataCollectionMapper.selectAllTableNames(schemaName.toLowerCase()); //大写最好
        //    if (CollUtil.isEmpty(tableNames)) {
        //        return AjaxResult.error("模式: {} 下没有找到任何数据库表");
        //    }
        //
        //    Map<String, String> resultMap = tableNames
        //            .stream()
        //            .map(String::toUpperCase)
        //            .filter(name -> name.contains("SWDATA_OBS_DEN_"))
        //            .filter(name -> !name.contains("GRACEA_SZ2") && !name.contains("GRACEA_GRACEB"))
        //            .collect(Collectors.toMap(name -> name.replace("SWDATA_OBS_DEN_", ""), name -> name));
        //    return AjaxResult.success(resultMap);
        //} catch (Exception e) {
        //    e.printStackTrace();
        //    return AjaxResult.error(e.getMessage());
        //}finally {
        //    if (connection != null) {
        //        connection.close();
        //    }
        //}


        // update by zw 由检查表 改为直接查 字典
        String dictType = "air_den_satellite";
        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
        if (null != data) {
            //按自然语言排序
            Map<String, String> resMap = new TreeMap<>();
            for (SysDictData datum : data) {
                String satellite = datum.getDictValue();
                //排除fy3c
                if ("FY3C".equals(satellite)) {
                    continue;
                }
                String label = datum.getDictLabel();
                String tableName = "SWDATA_OBS_DEN_"+satellite.toUpperCase();
                resMap.put(label,tableName);
            }
            return AjaxResult.success(resMap);
        }else {
            return AjaxResult.success();
        }
    }

    @SneakyThrows
    @ApiOperation(value = "获取范围及对应的卫星列表", notes = "根据 时间范围 和 高度范围 获取范围及对应的卫星列表")
    @PostMapping("/sateList")
    public AjaxResult sateList(@RequestBody ModelAssessDiffAltVo vo) {

        //	python /dgdq/code/dgdq-algorithm/modelAssess/different_heights_cal_density/code/get_hight_statellite_data.py
        //	2015-01-01 2015-01-10 400 500
        //	SWDATA_OBS_DEN_GRACEB,SWDATA_OBS_DEN_CHAMP,SWDATA_OBS_DEN_GRACEA,SWDATA_OBS_DEN_GOCE,SWDATA_OBS_DEN_GRACEFO,SWDATA_OBS_DEN_SWARMA,SWDATA_OBS_DEN_SWARMB,SWDATA_OBS_DEN_SWARMC
        //获取模式名
        String schemaName;
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            String schema = connection.getSchema();
            if (StrUtil.isNotBlank(schema)) {
                schemaName = schema;
            } else {
                schemaName = connection.getMetaData().getUserName();
            }
            List<String> tableNames = dataCollectionMapper.selectAllTableNames(schemaName.toLowerCase()); //大写最好
            if (CollUtil.isEmpty(tableNames)) {
                return AjaxResult.error("模式: {} 下没有找到任何数据库表");
            }

            String tableNameStr = tableNames
                    .stream()
                    .map(String::toUpperCase)
                    .filter(name -> name.contains("SWDATA_OBS_DEN_"))
                    .filter(name -> !name.contains("GRACEA_SZ2") && !name.contains("GRACEA_GRACEB"))
                    .collect(Collectors.joining(","));

            String codePath = PathUtil.codeModuleDir(BizModule.MODEL_ASSESS) + PathUtil.moduleDirs().getModelAssess().get("diff_alt_sates");
            String[] altArr = vo.getAltitude().split(",");
            String command = "python " + codePath + " "
                    + vo.getStartTime() + " "
                    + vo.getEndTime() + " "
                    + altArr[0] + " "
                    + altArr[1] + " "
                    + tableNameStr;
            String results = ExecUtil.execCmdWithResult(command);
            if (results == null || !results.contains("###")) {
                return AjaxResult.error(results);
            }
            String jsonStr = results.split("###")[1];
            Map<String, Object> altAndTableNameMap = JSONUtil.toBean(jsonStr, Map.class);
            Map<String, Object> resultMap = altAndTableNameMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
                String nameArr = entry.getValue().toString(); // ["SWDATA_OBS_DEN_SWARMB"]
                if (nameArr.contains("None")){
                    return "None";
                }
                return JSONUtil.parseArray(nameArr).toList(String.class)
                        .stream()
                        .map(name -> {
                            return MapUtil.builder(new HashMap<>())
                                    .put("label", name.replace("SWDATA_OBS_DEN_", ""))
                                    .put("value", name)
                                    .build();
                        }).collect(Collectors.toList());
            }));

            return AjaxResult.success(resultMap);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }finally {
            if (null != connection) {
                connection.close();
            }
        }
    }

    @ApiOperation(value = "获取计算评估结果", notes = "获取计算评估结果-最新")
    @PostMapping("/cal")
    public AjaxResult calculateRes(@Validated @RequestBody ModelAssessDiffAltVo vo) throws ProcessStopException {


        String codePath = PathUtil.codeModuleDir(BizModule.MODEL_ASSESS) + PathUtil.moduleDirs().getModelAssess().get("diff_alt");
        String baseDir = PathUtil.tmpModuleDir(BizModule.MODEL_ASSESS) + "/diff_alt_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMAT);

//    {
//        "satellite":"0~400:SWDATA_OBS_DEN_GRACEA,401~700:SWDATA_OBS_DEN_GRACEB",  //这个前端组装好 java用字符串接
//            "startTime":"2005-01-01",
//            "endTime":"2005-01-10",
//            "interval":1440,
//            "model":["JB2008","nrlmsise00","DTM2020","Jacchia71"]
//    }
        Map<String, Object> paramMap = MapUtil.builder(new HashMap<String, Object>())
                .put("satellite", vo.getSatellite())
                .put("startTime", vo.getStartTime())
                .put("endTime", vo.getEndTime())
                .put("interval", Integer.valueOf(vo.getInterval()))
                .put("model", vo.getModel())
                .build();

        String paramJsonPath = baseDir + "/input_param.json";
        FileWriter.create(new File(paramJsonPath)).write(JSONUtil.toJsonPrettyStr(paramMap));

        List<String> command = Stream.of("python",
                "-u",
                codePath,
                paramJsonPath,
                baseDir
        ).collect(Collectors.toList());

        String sseId = vo.getSseId()+ SecurityUtils.getLoginUser().getUsername();
        String results = ExecUtil.execPython(command, sseId);
        SseEmitterUtils.sendMessage(sseId, "100");
        SseEmitterUtils.remove(sseId);

        String txtPath = results.split("###")[1];
        String content = CommonUtil.readStrData(new File(txtPath), null);
        Map<String, JSONObject> contentMap = JSONUtil.toBean(content, Map.class);

        Map<String,Object> modelDataMap = (Map<String,Object>)contentMap.get("modelData");

        List<String> altNameList = Arrays.asList(vo.getSatellite().split(","))
                .stream()
                .map(o -> o.split(":")[0].replace("morethan", "以上") + "km")
                .collect(Collectors.toList());


        Map<String,Object> resultMap = new HashMap<>();

        //1.观测值和参评模型模拟值分布结果图
        Map<String,Object> simuMap = new HashMap<>();
        Map<String,Object> altAndTimeListMap = new HashMap<>();
        List<String> emptyList = new ArrayList<>();
        altNameList.forEach(altName -> {
            Map<String,Object> simuDataMap = (Map<String,Object>)modelDataMap.get(altName + "_obs_Simu");
            if (null != simuDataMap) {
                Object obsData = modelDataMap.get(altName + "_obs_Density");
                Object timeList = modelDataMap.get(altName + "_obs_Time");

                simuDataMap.put("obs",obsData);
                simuDataMap.put("time",timeList);

                altAndTimeListMap.put(altName, timeList); //顺便记录下时间数组，后面要用
                simuMap.put(altName, simuDataMap);
            }else {
                emptyList.add(altName);
            }
        });
        //移除空值
        resultMap.put("simu", simuMap);
        altNameList.removeAll(emptyList);

        Map<String,Object> errorDataMap = (Map<String,Object>) contentMap.get("errorData");

        //2.收集 图2 、图3 所需的数据信息

        Map<String,Map<String,Map<String,Object>>> absoluteRelativeMap = new HashMap<>();
        Map<String,Map<String,Object>> coeeMap = new HashMap<>();

        // 这三个errorType都是 模型、站点、resultType
        Map<String,Map<String,Map<String,Object>>> rootMap = new HashMap<>();
        Map<String,Map<String,Map<String,Object>>> absoluteErrorMap = new HashMap<>();
        Map<String,Map<String,Map<String,Object>>> relativeErrorMap = new HashMap<>();

        errorDataMap.entrySet().forEach(entry -> {
            String key = entry.getKey();
            Object value = entry.getValue();

            //收集图2 所需的数据信息
            if (key.contains("absolute_error#result") || key.contains("relative_error#result")){
                //"0-400km#JB2008#relative_error#result"
                key = key.replace("_error#result", "");
                String[] mapKeyArr = key.split("#");
                String altName = mapKeyArr[0];
                String model = mapKeyArr[1];
                String errorType = mapKeyArr[2];

                Map<String, Map<String, Object>> errorAndModelMap = absoluteRelativeMap.get(altName);
                if (CollUtil.isEmpty(errorAndModelMap)){
                    errorAndModelMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(model, value);
                    dataMap.put("time", altAndTimeListMap.get(altName));
                    errorAndModelMap.put(errorType, dataMap);
                    absoluteRelativeMap.put(altName, errorAndModelMap);
                }else {
                    Map<String, Object> dataMap = errorAndModelMap.get(errorType);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(model, value);
                    dataMap.put("time", altAndTimeListMap.get(altName));
                    errorAndModelMap.put(errorType, dataMap);
                    absoluteRelativeMap.put(altName, errorAndModelMap);
                }
                return;
            }

            //收集图3 和 表格 所需的数据信息
            // key = "0-400km#nrlmsise00#absolute_error#confidenceInterval"
            String[] mapKeyArr = key.split("#");
            String altName = mapKeyArr[0];
            String model = mapKeyArr[1];
            String errorType = mapKeyArr[2];

            if ("corrCoee".equalsIgnoreCase(errorType)){ // "0-400km#JB2008#corrCoee" 相关系数是3层，所以就一个简单MAP
                Map<String, Object> altAndCoee = coeeMap.get(model);
                if (CollUtil.isEmpty(altAndCoee)){
                    altAndCoee = new HashMap<>();
                }
                altAndCoee.put(altName, value);
                coeeMap.put(model, altAndCoee); //前端希望是个数组
                return;
            }

            String resultType = mapKeyArr[3];

            // 这三个errorType都是 模型、站点、resultType
            if ("root_mean_square_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = rootMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    rootMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    rootMap.put(model, altAndResultMap);
                }
            }

            if ("relative_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = relativeErrorMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    relativeErrorMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    relativeErrorMap.put(model, altAndResultMap);
                }
            }

            if ("absolute_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = absoluteErrorMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    absoluteErrorMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    absoluteErrorMap.put(model, altAndResultMap);
                }
            }
        });

        //3.大气模型误差结果统计图
        resultMap.put("absolute_relative",absoluteRelativeMap);
        //4.大气模型误差评估结果统计图
        Map<String, Object> resultErrorMap = new HashMap<>();
        resultErrorMap.put("x", altNameList);

        //<model,<location,resultType>>
        Map<String, List<Object>> avg_absolute = absoluteErrorMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                if (null ==absoluteErrorMap.get(entry.getKey()).get(altName)) {
                    return ListUtil.empty();
                }
                return absoluteErrorMap.get(entry.getKey()).get(altName).get("averageResult");
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> avg_relative = relativeErrorMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                if (null ==relativeErrorMap.get(entry.getKey()).get(altName)) {
                    return ListUtil.empty();
                }
                return relativeErrorMap.get(entry.getKey()).get(altName).get("averageResult");
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> coee = coeeMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                return coeeMap.get(entry.getKey()).get(altName);
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> root = rootMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                if (null ==rootMap.get(entry.getKey()).get(altName)) {
                    return ListUtil.empty();
                }
                return rootMap.get(entry.getKey()).get(altName).get("confidenceInterval");
            }).collect(Collectors.toList());
        }));

        resultErrorMap.put("avg_absolute", avg_absolute);
        resultErrorMap.put("avg_relative", avg_relative);
        resultErrorMap.put("coee", coee);
        resultErrorMap.put("root", root);

        resultMap.put("error_data",resultErrorMap);

        //表格开始
        List<Map<String,Object>> tableData = new ArrayList<>();
        List<String> modelList = rootMap.entrySet().stream().map(Map.Entry::getKey).distinct().collect(Collectors.toList());
        altNameList.forEach( altName -> {
            modelList.forEach( model -> {
                Map<String, Object> dataMap = MapUtil.builder(new HashMap<String, Object>())
                        .put("alt", altName)
                        .put("model", model)
                        .put("corrCoee", coeeMap.get(model).get(altName))
                        .put("root", null==rootMap.get(model).get(altName)?ListUtil.empty():rootMap.get(model).get(altName).get("confidenceInterval"))
                        .put("avgAbsolute", null ==absoluteErrorMap.get(model).get(altName)?ListUtil.empty(): absoluteErrorMap.get(model).get(altName).get("confidenceInterval"))
                        .put("avgRelative", null ==relativeErrorMap.get(model).get(altName)?ListUtil.empty(): relativeErrorMap.get(model).get(altName).get("confidenceInterval"))
                        .build();
                tableData.add(dataMap);
            });
        });
        resultMap.put("tableData", tableData);

        //3. 最佳策略结果

        String bestModelStrategyAll = JSONUtil.parseArray(contentMap.get("bestModel")).toList(String.class).get(0);
        Map<String,Map<String,String>> strategyMap = JSONUtil.toBean(bestModelStrategyAll, Map.class);
        List<String> bestModelStrList = strategyMap.entrySet().stream().map(entry -> {
            String altName = entry.getKey();
            Map<String, String> bestModelMap = entry.getValue();
            String prefixStr = altName + "高度范围最优模型策略为：\n";

            String suffixStr = bestModelMap.entrySet()
                    .stream()
                    .map(s -> vo.translateErrorType(s.getKey()) + s.getValue())
                    .collect(Collectors.joining(";\n"));
            return prefixStr + suffixStr;
        }).collect(Collectors.toList());
        resultMap.put("bestModel", bestModelStrList);
        resultMap.put("txtPath", txtPath);

        SseEmitterUtils.sendMessage(sseId,"100");
        SseEmitterUtils.remove(sseId);

        //4. txt文件也要加上
        List<String> list = Stream.of(txtPath).collect(Collectors.toList());
        resultMap.put("resultData", JSONUtil.toJsonStr(list));
        resultMap.put("module", ResultSaveModule.MODEL_ASSESS);
        resultMap.put("submodule", ResultSaveModule.MODEL_ASSESS_ALT);
        return AjaxResult.success(resultMap);

    }


    //@PreAuthorize("@ss.hasPermi('me:differentAltitude:subscribe')")
    @ApiOperation(value = "建立连接", notes = "建立连接")
    @GetMapping(path = "subscribe", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})
    public SseEmitter push(String id, String flag) {
        return differentAltitudeService.push(id,flag);
    }




}
